from io import StringIO
import time
import httplib
import json
import re
import phelections
import os

def getRegion(fp):
    while True:
        line = fp.readline()[:-1]
        if 'REGION:' in line:
            break
    return line[0:-1 * (len('REGION:'))]

HEADER_LINES_IN = [
                "Page ",
                ]
HEADER_LINES = [
                "CLUSTER/",
                "GROUP",
                "NUMBER",
                "TOTAL NO. OF REG.",
                "VOTERS AFTER",
                "CLUSTERING/GROUPING",
                "CLUSTERED/",
                "GROUPED",
                "PRECINCTS",
                "NAME AND ADDRESS",
                "OF POLLING PLACE",
                "BARANGAY",
                "ESTABLISHED",
                "PRECINCT",
                "TOTAL NUMBER",
                "OF REGISTERED",
                "VOTERS",
                ]
PAGE_END_LINE_MARKER = "Total No. of Barangays:"
PAGE_TOTALS_LINE_MARKER = "Total No. of Voting Centers:"

def ignoreLine(line):
    for test in HEADER_LINES:
        if test == line.strip():
            return True
    for test in HEADER_LINES_IN:
        if test in line.strip():
            return True
    return False

def cleanSplit(line, sep=" "):
    lineArr = line.split(sep)
    lineCleanArr = []
    for lineElem in lineArr:
        if lineElem.strip():
            lineCleanArr.append(lineElem.strip())
    return lineCleanArr

POST_ADDRESS_DATA_RE = re.compile("[0-9]+[A-Z][ ]+[0-9]+.*")

def getPrecinctNoList(fp, line, last_address):
    address = ""
    while True:
        if not line:
            line = fp.readline()[:-1]
        if ignoreLine(line):
            line = ""
            continue
#        lineCleanArr = cleanSplit(line)
        if POST_ADDRESS_DATA_RE.match(line):
            break
#        allDigit = True
#        for lineElem in lineCleanArr:
#            if not str(lineElem.strip()[0:1]).isdigit():
#                allDigit = False
#                break
#        if allDigit:
#            break
        address += (line.strip() + " ")
        line = ""
    if not address.strip():
        address = last_address

    last_line = ""
    lineCleanArr = cleanSplit(line)

    precinctNoAndVoterCount = {}
    # Get the first precinct No and voters count and cluster number
    precinctNoAndVoterCount[lineCleanArr[0]] = int(lineCleanArr[1])
    totalVotersCrossCheck = int("".join(lineCleanArr[1].split(",")))

    # See Bangui, Ilocos Norte:  The data does not have the right part
    precinctNoList = []
    clusteredPrecinctVotersCount = 0
    clusterPrecinctNo = 0
    if len(lineCleanArr) > 2:
        clusterPrecinctNo = int("".join(lineCleanArr[2].split(",")))

        actualData = lineCleanArr[-3:]
        actualData.reverse()
        foundEarly = False
        i = 0
        for lineElem in actualData:
            clean = lineElem.split(",")[0].strip()
            if len(clean) == 5:
                precinctNoList.append(clean)
            elif i == 0:
                if clean.isdigit():
                    clusteredPrecinctVotersCount = int("".join(lineElem.split(",")))
                else:
                    print "WARNING: (1) Voters After Cluster - Row (" + clean + ")"
                foundEarly = True
            i += 1

        # There are next lines until the total
        while not foundEarly:
            i = 0
            line = fp.readline()[:-1]
            lineCleanArr = cleanSplit(line)
            for lineElem in lineCleanArr:
                clean = lineElem.split(",")[0].strip()
                if len(clean) == 5:
                    precinctNoList.append(clean)
                elif i == 0:
                    if clean.isdigit():
                        clusteredPrecinctVotersCount = int("".join(lineElem.split(",")))
                    else:
                        print "WARNING: (1) Voters After Cluster - Row (" + clean + ")"
                    foundEarly = True
                i += 1
        precinctNoList.sort()

    # Get the remainder of the Precinct Cluster Information
    while True:
        line = fp.readline()[:-1]
        if ignoreLine(line):
            continue
        lineCleanArr = cleanSplit(line)
        if len(lineCleanArr) > 3:
            last_line = line
            break
        # Check if it looks like text and not numbers -- must be a new address
        allDigits = True
        for lineElem in lineCleanArr:
            if not lineElem.strip()[0:1].isdigit():
                allDigits = False
                break
        if not allDigits:
            last_line = line
            break
        precinctNoAndVoterCount[lineCleanArr[0]] = int(lineCleanArr[1])
        totalVotersCrossCheck += int(lineCleanArr[1])

    return address, \
            clusterPrecinctNo, \
            precinctNoAndVoterCount, \
            precinctNoList, \
            clusteredPrecinctVotersCount, \
            totalVotersCrossCheck, \
            last_line


def getPrecinctAddress(fp):

    clusteredPrecincts = []
    last_line = ""
    last_address = ""
    while not 'SUB TOTAL ' in last_line:
        address, \
        clusterPrecinctNo, \
                precinctNoAndVoterCount, \
                precinctNoList, \
                clusteredPrecinctVotersCount, \
                totalVotersCrossCheck, \
                last_line = getPrecinctNoList(fp, last_line, last_address)
        last_address = address.strip()
        # This is not a real independent precinct -- it belongs to a previous one
        if clusterPrecinctNo == 0:
            found = {}
            for precinct in clusteredPrecincts:
                for precinctNo in precinctNoAndVoterCount:
                    if precinctNo in precinct['NoList']:
                        found = precinct
                        break
            if found:
                for precinctNo in precinctNoAndVoterCount:
                    found['precinctVoters'][precinctNo] = precinctNoAndVoterCount[precinctNo]
                    found['totalVotersCrossCheck'] += precinctNoAndVoterCount[precinctNo]
            else:
                print "WARNING: Not Previously Defined PrecinctNos [%s]" % (
                    ",".join( [ x for x in precinctNoAndVoterCount.keys() ] ))
                clusteredPrecincts.append(
                    {
                    'clusterNo' : clusterPrecinctNo,
                    'address' : address.strip(),
                    'precinctVoters' : precinctNoAndVoterCount,
                    'NoList' : precinctNoList,
                    'clusteredVoterCount' : clusteredPrecinctVotersCount,
                    'totalVotersCrossCheck' : totalVotersCrossCheck,
                     })
        else:
            clusteredPrecincts.append(
                {
                'clusterNo' : clusterPrecinctNo,
                'address' : address.strip(),
                'precinctVoters' : precinctNoAndVoterCount,
                'NoList' : precinctNoList,
                'clusteredVoterCount' : clusteredPrecinctVotersCount,
                'totalVotersCrossCheck' : totalVotersCrossCheck,
                 })

    # Reshuffle precincts based on PrecinctNoList -- the authoritative source
    for precinct in clusteredPrecincts:
        for precinctNo in precinct['NoList']:
            if not precinctNo in precinct['precinctVoters']:
                found = {}
                for precinctInner in clusteredPrecincts:
                    if (precinctInner['clusterNo'] != precinct['clusterNo'] and
                            precinctNo in precinctInner['precinctVoters']):
                        found = precinctInner
                        break
                if found:
                    precinct['precinctVoters'][precinctNo] = found['precinctVoters'][precinctNo]
                    precinct['totalVotersCrossCheck'] += precinct['precinctVoters'][precinctNo]
                    found['totalVotersCrossCheck'] -= precinct['precinctVoters'][precinctNo]
                    del found['precinctVoters'][precinctNo]
                else:
                    print "WARNING: Post-Shuffle - Cannot Find Precinct No [%s]" % precinctNo

    for precinct in clusteredPrecincts:
        # Run CrossChecks
        if len(precinct['precinctVoters']) != len(precinct['NoList']):
            print "WARNING: Number of elements not same: PrecinctVoter(%d) != PrecinctList(%d)" % (
                    len(precinct['precinctVoters']),
                    len(precinct['NoList']))
        for precinctNo in precinct['NoList']:
            if not precinctNo in precinct['precinctVoters']:
                print "WARNING: Not in PrecinctVoter[%s]" % precinctNo
        for precinctNo in precinct['precinctVoters']:
            if not precinctNo in precinct['NoList']:
                print "WARNING: Not in PrecinctList[%s]" % precinctNo
        if precinct['totalVotersCrossCheck'] != precinct['clusteredVoterCount']:
            print "WARNING: Total of Voters Count Not Equan [%d != %d]" % (
                precinct['totalVotersCrossCheck'],
                precinct['clusteredVoterCount'])

    line = last_line
    while not 'SUB TOTAL ' in line:
        line = fp.readline()[:-1]
        if ignoreLine(line):
            continue

    # SubTotal Line -- use as cross check if we got all the data we need
    subtotalParts = cleanSplit(line, "S")
    precinctTotal = int(subtotalParts[0].strip())
    votersTotal = int(cleanSplit(subtotalParts[1])[2].strip())
    precinctClusters = int(cleanSplit(subtotalParts[1])[3].strip())
    actualPrecinctTotal = 0
    actualVotersTotal = 0
    actualPrecinctClusters = 0
    for precinct in clusteredPrecincts:
        actualPrecinctTotal += len(precinct['NoList'])
        actualVotersTotal += precinct['clusteredVoterCount']
        actualPrecinctClusters += 1

    if precinctTotal != actualPrecinctTotal:
        print "WARNING: Number of Precincts Not Tallying [%d != %d]" % (
            precinctTotal, actualPrecinctTotal)
    if votersTotal != actualVotersTotal:
        print "WARNING: Number of Voters Not Tallying [%d != %d]" % (
            votersTotal, actualVotersTotal)
    if precinctClusters != actualPrecinctClusters:
        print "WARNING: Number of Clustered Precincts Not Tallying [%d != %d]" % (
            precinctClusters, actualPrecinctClusters)
    return clusteredPrecincts, line



def getBarangay(fp, last_num, last_line="", city=""):
    while True:
        line = fp.readline()[:-1]
        if line.startswith(HEADER_LINES[-1]):
            line = fp.readline()[:-1]
            break
        if ignoreLine(line):
            continue
        if PAGE_END_LINE_MARKER in line:
            return None, None
        if 'SUB TOTAL ' in last_line:
            break
    if PAGE_END_LINE_MARKER in line:
        return None, None
    nameArr = cleanSplit(line)
    barangayData = {}
    barangayData['name'] = (" ".join(nameArr[0:-1])).strip()
    barangayData['num'] = int(nameArr[-1].strip())

    # Get next line (address)
    barangayData['precinct'], last_line = getPrecinctAddress(fp)
#    if not (city in barangayData['address'] or barangayData['name'] in barangayData['address']):
#        print "WARNING: City[%s] or Brgy[%s] Not in Address[%s]." % (
#            city,
#            barangayData['name'],
#            barangayData['address'])

    return barangayData, last_line

def printBarangayData(data):
    print "-[%2d]  - [%s]" % (data['num'], data['name'])
    for precinct in data['precinct']:
        print "          +[%d] [%s]" % (precinct['clusterNo'],precinct['address'])
        print "          *     [%4d]  [%s]" % (precinct['clusteredVoterCount'],
                                "|".join([ "%s:%s" % (x, precinct['precinctVoters'][x]) for x in precinct['NoList']]),)

if __name__ == "__main__":
    try:
        fp = open(os.sys.argv[1]);
    except:
        exit(1)

    provinceData = {}
    provinceData['region'] = getRegion(fp)
    provinceData['province'] = fp.readline()[:-1]
    provinceData['city'] = fp.readline()[:-1]

    barangay = []
    i = 0
    last_line = ""
    while True:
        rowInfo, last_line = getBarangay(fp, i, last_line, provinceData['city'])
        if not rowInfo:
            break
        barangay.append(rowInfo)
        i += 1
        if rowInfo['num'] != i:
            print "WARNING: Skipped Row: %d  (Next: %d) " % (i, rowInfo['num'])



    # Finally Reshuffle Precincts if they have no "List"
    for barangayRow in barangay:
        for precinct in barangayRow['precinct']:
            if not precinct['NoList']:
                # Case where the precinct is not clustered (left alone)
                for precinctNo in precinct['precinctVoters']:
                    found = {}
                    for barangayRowInner in barangay:
                        for precinctInner in barangayRowInner['precinct']:
                            if (precinctNo in precinctInner['NoList']):
                                found = precinctInner
                                break
                    if found:
                        found['precinctVoters'][precinctNo] = precinct['precinctVoters'][precinctNo]
                        found['totalVotersCrossCheck'] += precinct['precinctVoters'][precinctNo]
                    else:
                        print "WARNING: Post-Post-Shuffle - Still Cannot Find Precinct No [%s]" % precinctNo
                barangayRow['precinct'].remove(precinct)

    finalBarangayRows = []
    for barangayRow in barangay:
        if barangayRow['precinct']:
            finalBarangayRows.append(barangayRow)


    for barangayRow in finalBarangayRows:
        for precinct in barangayRow['precinct']:
            # Run CrossChecks
            if len(precinct['precinctVoters']) != len(precinct['NoList']):
                print "WARNING: Number of elements not same: PrecinctVoter(%d) != PrecinctList(%d)" % (
                        len(precinct['precinctVoters']),
                        len(precinct['NoList']))
            for precinctNo in precinct['NoList']:
                if not precinctNo in precinct['precinctVoters']:
                    print "WARNING: Not in PrecinctVoter[%s]" % precinctNo
            for precinctNo in precinct['precinctVoters']:
                if not precinctNo in precinct['NoList']:
                    print "WARNING: Not in PrecinctList[%s]" % precinctNo
            if precinct['totalVotersCrossCheck'] != precinct['clusteredVoterCount']:
                print "WARNING: Total of Voters Count Not Equal [%d != %d]" % (
                    precinct['totalVotersCrossCheck'],
                    precinct['clusteredVoterCount'])

    for barangayInfo in finalBarangayRows:
        printBarangayData(barangayInfo)

    provinceData['data'] = finalBarangayRows


#    print json.dumps(provinceData, sort_keys=True, indent=4)
    finalData = json.dumps(provinceData, separators=(',',':'))

    if True:
        # Send to Server
#        conn = httplib.HTTPConnection("free-juan.appspot.com")
        conn = httplib.HTTPConnection("localhost:8888")

        headers = {"Content-type" : "application/x-www-form-urlencoded",
                   "Accept" : "text/plain"}
        conn.request("POST", "/MapYourPrecinct/postdata", finalData, headers)
        response = conn.getresponse()
        if response.status != 200:
            print "WARNING: Posting failure (%s)" % response.reason
        else:
            data = response.read()
            conn.close()
        time.sleep(1);
    else:
        for startOffset in range(0, len(finalBarangayRows), 15):
            if startOffset != 0:
                time.sleep(1);
            data_piece = json.load(StringIO(finalData))
            data_piece['data'] = data_piece['data'][startOffset:(
                startOffset + 15 > len(finalBarangayRows) and
                len(finalBarangayRows) or startOffset + 15)]
            data_pieceStr = json.dumps(data_piece, separators=(',',':'))

            # Send to Server
            conn = httplib.HTTPConnection("free-juan.appspot.com")
        #    conn = httplib.HTTPConnection("localhost:8888")

            headers = {"Content-type" : "application/x-www-form-urlencoded",
                       "Accept" : "text/plain"}
            conn.request("POST", "/MapYourPrecinct/postdata", data_pieceStr, headers)
            response = conn.getresponse()
            if response.status != 200:
                print "WARNING: Posting failure (%s)" % response.reason
            else:
                data = response.read()
                conn.close()
